/** \file
* Declarations for the generic Twitter database class.
* This file is part of Chorus, available at
* http://code.google.com/p/chorus-twitter/ . This file and all associated files
* and derivative works are bound by the terms of the LGPL 3.0 license,
* available at http://www.gnu.org/licenses/lgpl-3.0-standalone.html .
* \par Subversion URL:
* \$URL: http://chorus-twitter.googlecode.com/svn/branches/cpp/src/xml/XmlTree.hpp $
* \version \$Rev: 153 $
* \date    \$Date: 2010-04-06 07:43:42 +0000 (Tue, 06 Apr 2010) $
*/

#pragma once

#include <map>
#include <libxml/xmlstring.h>

#include "system/Thread.hpp"
#include "system/DebugConsole.hpp"

#include "xml/XmlElement.hpp"

/// The abstract untyped XML database.
class XmlTree
{
protected:
	/// The constructor. Cannot be called directly.
	XmlTree();

public:
	/// The destructor.
	virtual ~XmlTree();

public:
	/// Get the number of elements in the map.
	/// \return The number of elements in the map.
	virtual unsigned getNumElements() const = 0;

	/// Get an element by its ID.
	/// \param id The ID of the element.
	/// \return A pointer to the element, or 0 if no such element exists.
	virtual XmlElement *getElementById(unsigned id) = 0;

	/// Get the element tag for this tree type.
	/// \return The element tag string.
	virtual const xmlChar *getElementTag() const = 0;

	/// Get the XML element that's pending processing.
	/// \return A pointer to the pending element, or 0 if no element is
	///         pending.
	virtual XmlElement *getPendingElement() = 0;

	/// Create a new XML element that's pending processing.
	/// \return True for success.
	virtual bool newPendingElement() = 0;

	/// Add the pending element. This sets the pending element to zero and adds
	/// the element to the map if it succeeds.
	/// \return True for success.
	virtual bool addPendingElement() = 0;

	/// The XML/SAX handler for element start.
	/// \param name  The name of the element.
	/// \param attrs The list of attributes, alternating between name and value
	///              and ending with a null.
	virtual void startElement(const xmlChar *name, const xmlChar **attrs);

	/// The XML/SAX handler for element end.
	/// \param name  The name of the element.
	virtual void endElement(const xmlChar *name);

	/// The XML/SAX handler for element inner text.
	/// \param ch  The element's inner text.
	/// \param len The length of the element's inner text.
	virtual void elementText(const xmlChar *ch, unsigned len);


	/// Perform the initial data load. Asynchronous, so returns immediately.
	/// \return True for success.
	bool load();

	/// Update the database. Asynchronous, so returns immediately.
	/// \return True for success.
	bool update();

	/// Save the database. Asynchronous, so returns immediately.
	/// \return True for success.
	bool save();

protected:
	/// Perform the initial data load. Blocks until done.
	/// \return True for success.
	bool load_block();

	/// Update the database. Blocks until done.
	/// \return True for success.
	bool update_block();

	/// Save the database. Blocks until done.
	/// \return True for success.
	bool save_block();

	/// Perform the load operation.
	/// \return True for success.
	virtual bool loadPerform() = 0;

	/// Perform the update operation.
	/// \return True for success.
	virtual bool updatePerform() = 0;

	/// Perform the save operation.
	/// \return True for success.
	virtual bool savePerform() = 0;

public:
	/// The load thread.
	Thread threadload;
	/// The update thread.
	Thread threadupdate;
	/// The save thread.
	Thread threadsave;

public:
	/// The XML property pending processing.
	XmlProperty *currentProp;

	std::string propText;
};

/// The typed XML database. ElementType must be derived from XmlElement.
/// Uses virtual inheritance because of the diamond XmlTree -> TypedXmlTree/
/// DefaultTree -> StatusTree,UserTree,DmTree.
template <typename ElementType>
class TypedXmlTree : public virtual XmlTree
{
protected:
	/// The element map type.
	typedef typename std::map<unsigned, ElementType*> MapType;
	/// The element map iterator type.
	typedef typename MapType::iterator MapIterType;
	/// The element map constant iterator type.
	typedef typename MapType::const_iterator MapConstIterType;

protected:
	/// The constructor.
	TypedXmlTree()
	{
		pending = 0;
	}

public:
	/// The destructor.
	virtual ~TypedXmlTree()
	{
		for (MapIterType i = elms.begin();
			i != elms.end();
			i++)
		{
			if (i->second)
			{
				delete i->second;
				i->second = 0;
			}
		}
		if (pending)
		{
			delete pending;
			pending = 0;
		}
	}

public:
	/// Get the number of elements in the map.
	/// \return The number of elements in the map.
	virtual unsigned getNumElements() const
	{
		return elms.size();
	}

	/// Get an element by its ID.
	/// \param id The ID of the element.
	/// \return A pointer to the element, or 0 if no such element exists.
	virtual XmlElement *getElementById(unsigned id)
	{
		const MapIterType found = elms.find(id);
		if (found == elms.end())
			return 0;
		return found->second;
	}

	/// Create a new XML element that's pending processing.
	/// \return True for success.
	virtual bool newPendingElement()
	{
		if (pending)
		{
			delete pending;
			pending = 0;
		}
		pending = new ElementType;
		if (!pending)
		{
			PDEBUG("new failed");
			return false;
		}
		return true;
	}

	/// Get the XML element that's pending processing.
	/// \return A pointer to the pending element, or 0 if no element is
	///         pending.
	virtual XmlElement *getPendingElement()
	{
		return pending;
	}

	/// Add the pending element. This sets the pending element to zero and adds
	/// the element to the map if it succeeds.
	/// \return True for success.
	virtual bool addPendingElement()
	{
		if (!pending)
			return false;
		const unsigned id = pending->getId();
		MapConstIterType existing = elms.find(id);
		if (existing == elms.end())
		{
			std::pair<unsigned, ElementType*> newpair(id, pending);
			elms.insert(newpair);
			pending = 0;
			return true;
		}
		else
		{
			delete pending;
			pending = 0;
			return false;
		}
	}

protected:
	/// The map of IDs to their respective elements.
	MapType elms;
public:
	/// The XML element pending processing.
	ElementType *pending;
};
